package com.ylxin.smartplatform.websocket;

import com.ylxin.smartplatform.event.DeviceCommandEvent;
import com.ylxin.smartplatform.event.DeviceStatusEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * @author 29048
 * @version V1.0
 * @Project bk2205
 * @Package com.ylxin.smartplatform.websocket
 * @Description: Create by 杨立新 @Date 2022/9/15 20:45
 */

@Component
public class PowerCommandWebSocket extends TextWebSocketHandler {

    private ApplicationContext applicationContext;

    @Autowired
    public void setApplicationContext(ApplicationContext applicationContext){
        this.applicationContext = applicationContext;
    }

    private Map<String, WebSocketSession> allClients = new HashMap<>();

    /**
     * 建立连接之后
     * @param session
     * @throws Exception
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        //我们的最终目的时控制设备，所以我们必须知道设备的会话是什么，所以在这里我们必须把会话保存
        //我们还是约定在地址的最后一位拼接我们的设备的编号
        String path = session.getUri().getPath();
        //最后一位是设备的id
        String deviceId = path.substring(path.lastIndexOf("/") + 1);
        //为了方便后面获取deviceId，直接将id设置给会话
        session.getAttributes().put("deviceId",deviceId);

        //如果设备之前有会话，就关闭，如果多次连接把之前的关闭
        WebSocketSession currentSession = allClients.get(deviceId);
        if (currentSession != null && currentSession.isOpen()){
            currentSession.close();
        }
        //保存会话，以设备id作为key
        allClients.put(deviceId, session);
        //当设备上线后，我们需要更新设备的状态为在线，ONLINE是自己约定的就知道是在线还是离线
        applicationContext.publishEvent(new DeviceStatusEvent(deviceId, DeviceStatusEvent.Status.ONLINE));
    }

    /**
     * 连接关闭之后
     * @param session
     * @param status
     * @throws Exception
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        //如果一个连接关闭了，说明会话结束，应该从allClients中删除
        String deviceId = (String) session.getAttributes().get("deviceId");
        allClients.remove(deviceId);
        //allClient中删除设备之后还要更新设备的状态为离线状态。移除之后再推送一个更新设备状态的事件，OFFLINE是自己约定的就知道是在线还是离线
        applicationContext.publishEvent(new DeviceStatusEvent(deviceId, DeviceStatusEvent.Status.OFFLINE));
    }

    /**
     * 事件侦听器
     * @param event
     */
    @EventListener
    @Async
    public void onCommand(DeviceCommandEvent event){
        String deviceId = event.getDeviceId();
        //根据设备id获取到会话WebSocketSession
        WebSocketSession webSocketSession = allClients.get(deviceId);
        if(webSocketSession != null && webSocketSession.isOpen()){
            try {
                //public TextMessage(java.lang.CharSequence payload)  springframework.web.socket下的一个TextMessage类的构造方法
                webSocketSession.sendMessage(new TextMessage(event.getCommand()));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
